<script>
  Polymer({
    is: 'x-basic',
    properties: {
      notifyingValue: {
        type: Number,
        notify: true
      }
    }
  });
</script>

<dom-module id="x-compose">
  <template>
    <x-basic id="basic1" notifying-value="{{obj.value}}" attrvalue$="{{obj.value}}" othervalue="{{obj.value2}}"></x-basic>
    <x-basic id="basic2" notifying-value="{{obj.value}}" attrvalue$="{{obj.value}}"></x-basic>
  </template>
  <script>
    Polymer({
      is: 'x-compose',
      properties: {
        obj: {
          type: Object,
          notify: true
        }
      },
      observers: [
        'objSubpathChanged(obj.*)',
        'objValueChanged(obj.value)'
      ],
      created: function() {
        this.observerCounts = {
          objSubpathChanged: 0,
          objValueChanged: 0
        };
      },
      clearObserverCounts: function() {
        for (var i in this.observerCounts) {
          this.observerCounts[i] = 0;
        }
      },
      objSubpathChanged: function(change) {
        this.observerCounts.objSubpathChanged++;
        assert.equal(change.base, this.obj);
        if (this.expectedObjSubpath) {
          assert.equal(change.path, this.expectedObjSubpath);
          assert.equal(change.value, this.expectedObjValue);
        }
      },
      objValueChanged: function(value) {
        this.observerCounts.objValueChanged++;
        assert.equal(this.obj.value, value);
      }
    });
  </script>
</dom-module>

<dom-module id="x-forward">
  <template>
    <x-compose id="compose" obj="{{obj}}"></x-compose>
  </template>
  <script>
    Polymer({
      is: 'x-forward',
      properties: {
        obj: {
          type: Object,
          notify: true
        }
      },
      observers: [
        'objSubpathChanged(obj.*)',
        'objValueChanged(obj.value)'
      ],
      created: function() {
        this.observerCounts = {
          objSubpathChanged: 0,
          objValueChanged: 0
        };
      },
      clearObserverCounts: function() {
        for (var i in this.observerCounts) {
          this.observerCounts[i] = 0;
        }
        this.$.compose.clearObserverCounts();
      },
      objSubpathChanged: function(change) {
        this.observerCounts.objSubpathChanged++;
        assert.equal(change.base, this.obj);
        if (this.expectedObjSubpath) {
          assert.equal(change.path, this.expectedObjSubpath);
          assert.equal(change.value, this.expectedObjValue);
        }
      },
      objValueChanged: function(value) {
        this.observerCounts.objValueChanged++;
        assert.equal(this.obj.value, value);
      }
    });
  </script>
</dom-module>

<dom-module id="x-stuff">
  <template>
    <x-basic id="basic" notifying-value="{{nested.obj.value}}" attrvalue$="{{nested.obj.value}}"></x-basic>
    <x-compose id="compose" obj="{{nested.obj}}"></x-compose>
    <x-forward id="forward" obj="{{nested.obj}}"></x-forward>
    <div id="boundChild" computed-from-paths="{{computeFromPaths(a, nested.b, nested.obj.c)}}" array-length="{{data.length}}"></div>
  </template>
  <script>
    Polymer({
      is: 'x-stuff',
      properties: {
        computedFromPaths: {
          computed: 'computeFromPaths(a, nested.b, nested.obj.c)'
        }
      },
      observers: [
        'nestedSubpathChanged(nested.*)',
        'nestedObjChanged(nested.obj)',
        'nestedObjSubpathChanged(nested.obj.*)',
        'nestedObjValueChanged(nested.obj.value)',
        'multipleChanged(a, b, nested.obj.*)',
        'multiplePathsChanged(a, nested.b, nested.obj.c)',
        'arrayChanged(array.splices)',
        'arrayChangedDeep(array.*)',
        'arrayNoCollChanged(arrayNoColl.splices)',
        'arrayOrPropChanged(prop, array.splices)',
        'aChanged(a.*)',
        'bChanged(b.*)',
        'cChanged(c.*)'
      ],
      created: function() {
        this.observerCounts = {
          nestedSubpathChanged: 0,
          nestedObjChanged: 0,
          nestedObjSubpathChanged: 0,
          nestedObjValueChanged: 0,
          multipleChanged: 0,
          multiplePathsChanged: 0,
          arrayChanged: 0,
          arrayNoCollChanged: 0,
          arrayOrPropChanged: 0
        };
      },
      clearObserverCounts: function() {
        for (var i in this.observerCounts) {
          this.observerCounts[i] = 0;
        }
        this.$.compose.clearObserverCounts();
        this.$.forward.clearObserverCounts();
      },
      nestedSubpathChanged: function(change) {
        this.observerCounts.nestedSubpathChanged++;
        assert.equal(change.base, this.nested);
        if (this.expectedNestedSubpath) {
          assert.equal(change.path, this.expectedNestedSubpath);
          assert.equal(change.value, this.expectedNestedValue);
        }
      },
      nestedObjChanged: function(value) {
        this.observerCounts.nestedObjChanged++;
        assert.equal(this.nested.obj, value);
      },
      nestedObjSubpathChanged: function(change) {
        this.observerCounts.nestedObjSubpathChanged++;
        assert.equal(change.base, this.nested.obj);
        if (this.expectedNestedObjSubpath) {
          assert.equal(change.path, this.expectedNestedObjSubpath);
          assert.equal(change.value, this.expectedNestedObjValue);
        }
      },
      nestedObjValueChanged: function(value) {
        this.observerCounts.nestedObjValueChanged++;
        assert.equal(this.get('nested.obj.value'), value);
      },
      multipleChanged: function(a, b, nestedObjChange) {
        this.observerCounts.multipleChanged++;
        assert.equal(a, 'a');
        assert.equal(b, 'b');
        assert.equal(nestedObjChange.base, this.nested.obj);
        if (this.expectedNestedObjSubpath) {
          assert.equal(nestedObjChange.path, this.expectedNestedObjSubpath);
          assert.equal(nestedObjChange.value, this.expectedNestedObjValue);
        }
        assert.equal(nestedObjChange.base, this.nested.obj);
      },
      computeFromPaths: function(a, b, c) {
        assert.equal(a, this.a, 'computeFromNested `a` arg incorrect');
        assert.equal(b, this.nested.b, 'computeFromNested `b` arg incorrect');
        assert.equal(c, this.nested.obj.c, 'computeFromNested `c` arg incorrect');
        return a + b + c;
      },
      multiplePathsChanged: function(a, b, c) {
        this.observerCounts.multiplePathsChanged++;
        assert.equal(a, this.a, 'multiplePathsChanged `a` arg incorrect');
        assert.equal(b, this.nested.b, 'multiplePathsChanged `b` arg incorrect');
        assert.equal(c, this.nested.obj.c, 'multiplePathsChanged `c` arg incorrect');
      },
      arrayChanged: function(splices) {
        this.observerCounts.arrayChanged++;
        if (this.array.length) {
          assert.equal(splices.indexSplices.length, 1,
            'arrayChanged indexSplices incorrect');
          assert.equal(splices.indexSplices[0].addedCount, 3,
            'arrayChanged indexSplices incorrect');
          assert.equal(splices.keySplices.length, 1,
            'arrayChanged keySplices incorrect');
          assert.equal(splices.keySplices[0].added.length, 3,
            'arrayChanged keySplices incorrect');
        }
        this.arraySplices = splices;
      },
      arrayChangedDeep: function() { },
      arrayNoCollChanged: function(splices) {
        this.observerCounts.arrayNoCollChanged++;
        if (this.arrayNoColl.length) {
          assert.equal(splices.indexSplices.length, 1,
            'arrayNoCollChanged indexSplices incorrect');
          assert.equal(splices.indexSplices[0].addedCount, 3,
            'arrayNoCollChanged indexSplices incorrect');
        }
      },
      arrayOrPropChanged: function(prop) {
        this.observerCounts.arrayOrPropChanged++;
        assert.equal(prop, this.prop);
      },
      aChanged: function() {},
      bChanged: function() {},
      cChanged: function() {}
    });
  </script>
</dom-module>
